package com.cencat.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cencat.common.utils.CencatStringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * 加密工具类
 * 提供MD5、SHA、AES等加密算法
 * 
 * @author cencat
 * @since 2024-01-01
 */
public class EncryptUtils {
    
    private static final Logger log = LoggerFactory.getLogger(EncryptUtils.class);

    /**
     * 私有构造函数，防止实例化
     */
    private EncryptUtils() {
    }

    /**
     * MD5加密
     * 
     * @param data 待加密数据
     * @return 加密后的字符串
     */
    public static String md5(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return null;
        }
        
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            log.error("MD5加密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * MD5加密（带盐值）
     * 
     * @param data 待加密数据
     * @param salt 盐值
     * @return 加密后的字符串
     */
    public static String md5WithSalt(String data, String salt) {
        if (CencatStringUtils.isBlank(data)) {
            return null;
        }
        
        String saltedData = data + (salt != null ? salt : "");
        return md5(saltedData);
    }

    /**
     * SHA-1加密
     * 
     * @param data 待加密数据
     * @return 加密后的字符串
     */
    public static String sha1(String data) {
        return sha(data, "SHA-1");
    }

    /**
     * SHA-256加密
     * 
     * @param data 待加密数据
     * @return 加密后的字符串
     */
    public static String sha256(String data) {
        return sha(data, "SHA-256");
    }

    /**
     * SHA-512加密
     * 
     * @param data 待加密数据
     * @return 加密后的字符串
     */
    public static String sha512(String data) {
        return sha(data, "SHA-512");
    }

    /**
     * SHA加密通用方法
     * 
     * @param data      待加密数据
     * @param algorithm 算法名称
     * @return 加密后的字符串
     */
    private static String sha(String data, String algorithm) {
        if (CencatStringUtils.isBlank(data)) {
            return null;
        }
        
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(digest);
        } catch (NoSuchAlgorithmException e) {
            log.error("{}加密失败: {}", algorithm, e.getMessage());
            return null;
        }
    }

    /**
     * 生成AES密钥
     * 
     * @return Base64编码的密钥
     */
    public static String generateAESKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(256);
            SecretKey secretKey = keyGenerator.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (NoSuchAlgorithmException e) {
            log.error("生成AES密钥失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * AES加密
     * 
     * @param data 待加密数据
     * @param key  密钥（Base64编码）
     * @return 加密后的字符串（Base64编码）
     */
    public static String aesEncrypt(String data, String key) {
        if (CencatStringUtils.isBlank(data) || CencatStringUtils.isBlank(key)) {
            return null;
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(key);
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
            
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            
            byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception e) {
            log.error("AES加密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * AES解密
     * 
     * @param encryptedData 加密数据（Base64编码）
     * @param key           密钥（Base64编码）
     * @return 解密后的字符串
     */
    public static String aesDecrypt(String encryptedData, String key) {
        if (CencatStringUtils.isBlank(encryptedData) || CencatStringUtils.isBlank(key)) {
            return null;
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(key);
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
            
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            
            byte[] encrypted = Base64.getDecoder().decode(encryptedData);
            byte[] decrypted = cipher.doFinal(encrypted);
            return new String(decrypted, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("AES解密失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * Base64编码
     * 
     * @param data 待编码数据
     * @return 编码后的字符串
     */
    public static String base64Encode(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return null;
        }
        
        try {
            return Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("Base64编码失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * Base64解码
     * 
     * @param encodedData 编码数据
     * @return 解码后的字符串
     */
    public static String base64Decode(String encodedData) {
        if (CencatStringUtils.isBlank(encodedData)) {
            return null;
        }
        
        try {
            byte[] decoded = Base64.getDecoder().decode(encodedData);
            return new String(decoded, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("Base64解码失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 生成随机盐值
     * 
     * @param length 盐值长度
     * @return 随机盐值
     */
    public static String generateSalt(int length) {
        if (length <= 0) {
            length = 16;
        }
        
        try {
            SecureRandom random = new SecureRandom();
            byte[] salt = new byte[length];
            random.nextBytes(salt);
            return bytesToHex(salt);
        } catch (Exception e) {
            log.error("生成盐值失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 生成随机盐值（默认16位）
     * 
     * @return 随机盐值
     */
    public static String generateSalt() {
        return generateSalt(16);
    }

    /**
     * 密码加密（使用MD5+盐值）
     * 
     * @param password 原始密码
     * @param salt     盐值
     * @return 加密后的密码
     */
    public static String encryptPassword(String password, String salt) {
        if (CencatStringUtils.isBlank(password)) {
            return null;
        }
        
        // 使用SHA-256 + 盐值进行多次加密
        String encrypted = password;
        for (int i = 0; i < 3; i++) {
            encrypted = sha256(encrypted + (salt != null ? salt : ""));
        }
        return encrypted;
    }

    /**
     * 验证密码
     * 
     * @param password        原始密码
     * @param encryptedPassword 加密后的密码
     * @param salt            盐值
     * @return 是否匹配
     */
    public static boolean verifyPassword(String password, String encryptedPassword, String salt) {
        if (CencatStringUtils.isBlank(password) || CencatStringUtils.isBlank(encryptedPassword)) {
            return false;
        }
        
        String encrypted = encryptPassword(password, salt);
        return encryptedPassword.equals(encrypted);
    }

    /**
     * 生成UUID（去除横线）
     * 
     * @return UUID字符串
     */
    public static String generateUUID() {
        return java.util.UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 生成随机字符串
     * 
     * @param length 字符串长度
     * @return 随机字符串
     */
    public static String generateRandomString(int length) {
        if (length <= 0) {
            return "";
        }
        
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(length);
        
        for (int i = 0; i < length; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        
        return sb.toString();
    }

    /**
     * 生成数字验证码
     * 
     * @param length 验证码长度
     * @return 数字验证码
     */
    public static String generateNumericCode(int length) {
        if (length <= 0) {
            return "";
        }
        
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(length);
        
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        
        return sb.toString();
    }

    /**
     * 字节数组转十六进制字符串
     * 
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    /**
     * 十六进制字符串转字节数组
     * 
     * @param hex 十六进制字符串
     * @return 字节数组
     */
    public static byte[] hexToBytes(String hex) {
        if (CencatStringUtils.isBlank(hex) || hex.length() % 2 != 0) {
            return new byte[0];
        }
        
        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
        }
        return bytes;
    }

    /**
     * 简单的字符串混淆
     * 
     * @param data 待混淆数据
     * @param key  混淆密钥
     * @return 混淆后的字符串
     */
    public static String simpleObfuscate(String data, String key) {
        if (CencatStringUtils.isBlank(data) || CencatStringUtils.isBlank(key)) {
            return data;
        }
        
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < data.length(); i++) {
            char dataChar = data.charAt(i);
            char keyChar = key.charAt(i % key.length());
            sb.append((char) (dataChar ^ keyChar));
        }
        
        return Base64.getEncoder().encodeToString(sb.toString().getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 简单的字符串反混淆
     * 
     * @param obfuscatedData 混淆数据
     * @param key            混淆密钥
     * @return 原始字符串
     */
    public static String simpleDeobfuscate(String obfuscatedData, String key) {
        if (CencatStringUtils.isBlank(obfuscatedData) || CencatStringUtils.isBlank(key)) {
            return obfuscatedData;
        }
        
        try {
            String decodedData = new String(Base64.getDecoder().decode(obfuscatedData), StandardCharsets.UTF_8);
            StringBuilder sb = new StringBuilder();
            
            for (int i = 0; i < decodedData.length(); i++) {
                char dataChar = decodedData.charAt(i);
                char keyChar = key.charAt(i % key.length());
                sb.append((char) (dataChar ^ keyChar));
            }
            
            return sb.toString();
        } catch (Exception e) {
            log.error("字符串反混淆失败: {}", e.getMessage());
            return obfuscatedData;
        }
    }
}